
function cloneSlot(slot){
    const pack = createPackage({
        root: slot._root,
        args: slot._args,
        val: slot._val,
        target: slot._target,
        origin: slot._origin,
        from: slot._from,
    })
    return pack;
}
let packId = 0;

class Package {
    constructor({
        root,
        args,
        val,
        target,
        origin,
        from,
    }) {
        this._root = root;
        this._ctx = root.$runner;
        this._id = packId++;
        this._args = args;
        this._val = val;
        this._from = from || target;
        this._target = target;
        this._origin = origin || target;
        this._error = null;
        Object.keys(root.$data).forEach(key => {
            Object.defineProperty(this, key, {
                get() {
                    return root.data[key];
                },
                set(val) {
                    root.data[key] = val;
                }
            })
        })
        Object.freeze(this);
    }
    $clone(){
        return cloneSlot(this);
    }
    $next(...args) {
        return this.$branch('', ...args)
    }
    $toJSON () {
        return {
            id: this._id,
            origin: this._origin,
            from: this._from,
            target: this._target,
            args: this._args,
            val: this._val,
            error: this._error,
        }
    }
    async $branch(branch, ...args) {
        try {
            if (Array.isArray(branch)) {
                return branch.map(br => this.$branch(br, ...args));
            }
            let clone = this.$clone();
            clone._val = args;
            const nextTargets = this._root.$navigator.$next(clone._target, branch);
            if (nextTargets.length == 0) {
                return this._ctx.$endEach(clone);
            }
            await this._ctx.$afterEach(clone);
            nextTargets.forEach(async (target, idx) => {
                const lastIdx = nextTargets.length - 1;
                if (idx === lastIdx) {
                    clone._from = this._target;
                    clone._target = target;
                    clone._args = args;
                    clone._val = null;
                    return this._root.$runner.$run(clone);
                }
                clone = this.$clone();
                clone._from = this._target;
                clone._target = target;
                clone._args = args;
                clone._val = null;
                return this._ctx.$run(clone);
            })

        } catch (e) {
            console.log(e, '>>??')
            return this._ctx.$endEach(this);
        }
    }
}

function createPackage(option) {
    return new Package(option);
}

exports.createPackage = createPackage;
